package com.lixin.web.netty.handler;

import com.lixin.exception.AuthException;
import com.lixin.exception.TokenExpiredException;
import com.lixin.model.enums.Event;
import com.lixin.model.request.Request;
import com.lixin.web.jwt.JwtConstants;
import com.lixin.web.jwt.JwtService;
import com.lixin.web.netty.NettyUtil;
import com.lixin.web.netty.UserSessionManager;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Set;

/**
 * Description:
 * Copyright:   Copyright (c)2023
 * Company:     sci
 *
 * @author: 张李鑫
 * @version: 1.0
 * Create at:   2023-07-28 19:54:44
 * <p>
 * Modification History:
 * Date         Author      Version     Description
 * ------------------------------------------------------------------
 * 2023-07-28     张李鑫                     1.0         1.0 Version
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class WebSocketAuthHandler<T> extends SimpleChannelInboundHandler<Request<T>> {

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        log.info("channelActive:{}", ctx.channel().id());
    }


    @Autowired
    private JwtService jwtService;

    @Autowired
    private UserSessionManager userSessionManager;

    private final Set<String> eventSet=Event.toSet();

    /**
     * 在这里进行鉴权
     *
     * @param ctx
     * @param request
     */
    @Override
    public void channelRead0(ChannelHandlerContext ctx, Request<T> request) {
        String event = request.getEvent();
        //默认不是登录的事件都需要鉴权
        if (!eventSet.contains(event)) {
            String token = getToken(request);
            Integer userId = jwtService.getUserId(token);
            validateAndSetUserId(ctx.channel(), userId);
            //检查是否需要刷新即当前token是否失效
            if (jwtService.checkTokenNeedsRefresh(token, userId)) {
                throw new TokenExpiredException();
            }

        }
        ctx.fireChannelRead(request);
    }

    private String getToken(Request<T> request) {
        Map<String, String> headers = request.getHeaders();
        if (headers == null || !headers.containsKey(JwtConstants.AUTH_TOKEN_KEY)) {
            throw new AuthException();
        }
        //截取实际token
        return headers.get(JwtConstants.AUTH_TOKEN_KEY).substring(JwtConstants.AUTH_TOKEN_PRE.length());
    }


    /**
     * 如果获取不到uid则说明是一次重新连接的操作
     *
     * @param channel
     * @param userId
     */
    private void validateAndSetUserId(Channel channel, Integer userId) {
        if (NettyUtil.getAttr(channel, NettyUtil.UID) == null) {
            //检查会话中是否有相同的token 有的话可能是token被滥用了
            if (userSessionManager.getChannelByUserId(userId) != null) {
                jwtService.clearToken(userId);
                throw new TokenExpiredException();
            }
            NettyUtil.setAttr(channel, NettyUtil.UID, userId);
        }
    }
}
